/*
 * strong.c - yet another process string language
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#define CODE_SIZE  256
#define STACK_SIZE 256

enum {
  HALT, CHOMP, CHOP, CLONE, DROP, DUP, IF, LENGTH, LOWER, MATCH, NL, PRINT, UPPER
};
const char *Opcode[] = {
  "halt", "chomp", "chop", "clone", "drop", "dup", "if", "length", "lower",
  "match", "nl", "print", "upper", NULL
};

char      Buffer[BUFSIZ];
int       Code[CODE_SIZE] = { PRINT, HALT };
char      *Stack[STACK_SIZE];
int       PC, SP;

void
usage(void)
{
  puts("usage: strong ...");
  exit(0);
}

char *
pop(void)
{
  assert(SP > 0);
  return Stack[SP--];
}

void
push(char *str)
{
  assert(SP < STACK_SIZE);
  Stack[++SP] = str;
}

void
chomp(void)
{
  char *str, *s;

  str = pop();
  for (s = str; *s != '\0'; s++)
    ;
  if (*(s-1) == '\n')
    *(s-1) = '\0';
  push(str);
}

void
chop(void)
{
  char *str, *s;

  str = pop();
  for (s = str; *s != '\0'; s++)
    ;
  *(s-1) = '\0';
  push(str);
}

void
clone(void)
{
  const char *str = pop();
  push(str);
  push(strdup(str));
}

void
drop(void)
{
  pop();
}

void
dup(void)
{
  const char *str = pop();
  push(str);
  push(str);
}

void
if_test(void)
{
  const char *test = pop();
  if (*test == NULL)
    PC++;
}

void
length(void)
{
  const char *str = pop();
  const char ret[BUFSIZ];
  int len = strlen(str);
  sprintf(ret, "%d", len);
  push(ret);
}

void
lower(void)
{
  char *s;
  char *str = pop();
  
  for (s = str; *s; s++)
    *s = tolower(*s);

  push(str);
}

void
match(void)
{
  const char *str = pop();
  const char *line = pop();
  push(strstr(line, str));
}

void
nl(void)
{
  fputc('\n', stdout);
}

void
print(void)
{
  const char *line = pop();

  if (line)
    fputs(line, stdout);
}

void
upper(void)
{
  char *s;
  const char *str = pop();
 
  for (s = str; *s; s++)
    *s = toupper(*s);
  push(str);
}

void (*EntryPoint[])(void) = {
  HALT, chomp, chop, clone, drop, dup, if_test, length, lower, match, nl,
  print, upper,
};

int
lookup(const char *entry)
{
  int i;

  for (i = 0; Opcode[i]; i++)
    if (!strcasecmp(entry, Opcode[i]))
      return i;
  return -1;
}

void
compile(const char *source)
{
  int pc, op;
  const char *word;
  const char *sep = " \t\n";

  for (word = strtok(source, sep), pc = 0; word; word = strtok(NULL, sep), pc++)
    if ((op = lookup(word)) != -1)
      Code[pc] = op;
    else {
      fprintf(stderr, "Unknown opcode '%s'", word);
      exit(1);
    }
}
void
execute(int *code)
{
  for (PC=0; code[PC] != HALT; PC++)
    EntryPoint[code[PC]]();
}

void
list_code(int *code)
{
  int pc;

  for (pc = 0; code[pc] != HALT; pc++)
    puts(Opcode[code[pc]]);
  puts("halt");
}

int
main(int argc, char *argv[])
{
  char *line;

  if (*++argv && !strcmp(*argv, "-h"))
    usage();
  if (*argv && !strcmp(*argv, "-e"))
    if (*++argv) {
      compile(*argv);
      argv++;
    }
  if (*argv && !strcmp(*argv, "-l")) {
    list_code((int *)Code);
    argv++;
  }

  while (1) {
    line = fgets(Buffer, BUFSIZ, stdin);
    if (line == NULL) break;
    push(Buffer);
    execute((int *)Code);
   }

  return 0;
}
